﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBCore.DB;
using System.Data;
using System.Reflection;
using DBCore.Attr;
using System.Web;

namespace DBCore
{
    /// <summary>
    /// 数据访问 调用基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseBLL<T> : IBLL<T> where T : Entity
    {
        public readonly DateTime defaultdate = new DateTime(2000, 1, 1);

        private IDAO dao;
        protected List<PropertyInfo> _propertys;  //存放类型T 的所有属性(property)
        protected Type _type;
        protected EntityAttribute _entityattr;   //T的Attribute
        protected Dictionary<PropertyInfo, EntityPropertyAttribute> _propertyattrs = new Dictionary<PropertyInfo, EntityPropertyAttribute>();
        
        public BaseBLL()
        {
            try
            {
                _type = typeof(T);
                _propertys = new List<PropertyInfo>(IOC.GetProperties(_type));
                _propertyattrs = IOC.EntityPropertyAttributes[_type];
                _entityattr = IOC.EntityAttributes[_type];

                if (HttpRuntime.Cache[_entityattr.Name] != null)
                {
                    dao = HttpRuntime.Cache[_entityattr.Name] as IDAO;
                }
                else
                {
                    dao = DAOFactory.CreateDao(_type);
                    dao.ConnectString = Database.DBS[_entityattr.Database].ConnectionString;
                    dao.TableName = _entityattr.Name;
                    HttpRuntime.Cache.Insert(dao.TableName, dao);
                }
            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }

        #region 私有方法
        /// <summary>
        /// 獲取關鍵字
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private DbParam GetPk(T c)
        {
            var par = new DbParam { Name = _entityattr.Primarykey };
            var p = _propertys.Find(pro => pro.Name.ToLower() == par.Name.ToLower());
            var value = p.GetValue(c, null);
            par.Value = value;
            par.DataType = _propertyattrs[p].DataType;
            return par;
        }
        /// <summary>
        /// 獲取DBParam的集合
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private DbParam[] GetPar(T c)
        {
            return (from kv in _propertyattrs where kv.Value.IsDbColumn let value = kv.Key.GetValue(c, null) select new DbParam(kv.Value.SqlName, value, kv.Value.DataType)).ToArray();
        }

        private DbParam[] GetParExceptKey(T c)
        {
            var entitypros = new List<DbParam>();
            foreach (var kv in _propertyattrs)
            {
                if (kv.Value.IsDbColumn && !(_entityattr != null && _entityattr.Primarykey != null && _entityattr.Primarykey.ToLower() == kv.Value.SqlName.ToLower()))
                {
                    var value = kv.Key.GetValue(c, null);
                    var par = new DbParam(kv.Value.SqlName, value, kv.Value.DataType);
                    entitypros.Add(par);
                }
            }
            return entitypros.ToArray();
        }

        private DbParam[] GetParExceptKeyWithoutNull(T c)
        {
            var entitypros = new List<DbParam>();
            foreach (var kv in _propertyattrs)
            {
                if (kv.Value.IsDbColumn && !(_entityattr != null && _entityattr.Primarykey != null && _entityattr.Primarykey.ToLower() == kv.Value.SqlName.ToLower()))
                {
                    var value = kv.Key.GetValue(c, null);
                    if (value != null)
                    {
                        if (kv.Value.DataType == DbDataType.DateTime)
                        {
                            if (value.ToString() != default(DateTime).ToString())
                            {
                                var par = new DbParam(kv.Value.SqlName, value, kv.Value.DataType);
                                entitypros.Add(par);
                            }
                        }
                        else if (kv.Value.DataType == DbDataType.BigInt)
                        {
                            if (value.ToString() != default(decimal).ToString())
                            {
                                var par = new DbParam(kv.Value.SqlName, value, kv.Value.DataType);
                                entitypros.Add(par);
                            }
                        }
                        else
                        {
                            var par = new DbParam(kv.Value.SqlName, value, kv.Value.DataType);
                            entitypros.Add(par);
                        }

                    }

                }
            }
            return entitypros.ToArray();
        }

        private DbParam[] GetParWithoutNull(T c)
        {
            var entitypros = new List<DbParam>();
            foreach (var kv in _propertyattrs)
            {
                if (kv.Value.IsDbColumn)
                {
                    var value = kv.Key.GetValue(c, null);
                    if (value != null)
                    {
                        var par = new DbParam(kv.Value.SqlName, value, kv.Value.DataType);
                        entitypros.Add(par);
                    }
                }
            }
            return entitypros.ToArray();
        }



        protected void CleanParameter(ref SqlParam[] myparams)
        {
            for (int i = 0; i < myparams.Length; i++)
            {
                var par = (DbParam)myparams.GetValue(i);
                if (par.Value == null || par.Value == DBNull.Value)
                {
                    myparams.SetValue(null, i);
                }
            }
        }
        #endregion
        #region 实现接口 通用方法
        /// <summary>
        /// 數據添加方法
        /// </summary>
        /// <param name="model">實體類</param>
        /// <returns></returns>
        public virtual int Add(T model)
        {
            var o = dao.Add(GetParWithoutNull(model));
            return o;
        }
        //done
        /// <summary>
        /// 批量添加實體數據
        /// </summary>
        /// <param name="model">實體的數據集List<T></param>
        /// <returns></returns>
        public virtual int AddRange(List<T> model)
        {
            List<DbParam[]> range = new List<DbParam[]>();
            foreach (var item in model)
            {
                range.Add(GetParWithoutNull(item));
            }

            var o = dao.AddRange(range);
            return o;
        }

        //done
        /// <summary>
        /// 數據更新
        /// </summary>
        /// <param name="model">數據實體 T</param>
        /// <returns></returns>
        public virtual int Update(T model)
        {
            var o = dao.Update(GetParExceptKey(model), GetPk(model));
            return o;
        }
        //done
        /// <summary>
        /// 更新數據,不更新實體中數據為空的列
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public virtual int UpdateWithoutNull(T model)
        {
            var o = dao.Update(GetParExceptKeyWithoutNull(model), GetPk(model));
            return o;
        }
        //done
        /// <summary>
        /// 根據實體刪除數據
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public virtual int DeleteByModel(T model)
        {
            if (model != null)
            {
                var pkpro = _propertys.Find(pro => pro.Name.ToLower() == _entityattr.Primarykey.ToLower());
                var o = dao.DeleteByKey(new DbParam(_entityattr.Primarykey, pkpro.GetValue(model, null), _propertyattrs[pkpro].DataType));
                return o;
            }
            else
            {
                return 0;
            }
        }
        //done
        /// <summary>
        /// 根據Key值刪除數據
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual int DeleteByKey(object key)
        {
            return DeleteByModel(GetDetailByKey(key));
        }
        //todo
        /// <summary>
        /// 根據Key值數組,批量刪除數據
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public virtual int DeleteByIds(object[] keys)
        {

            var pkpro = _propertys.Find(pro => pro.Name.ToUpper() == _entityattr.Primarykey.ToUpper());
            if (KeyIsNum(new DbParam(_entityattr.Primarykey, "", _propertyattrs[pkpro].DataType))) //在key值为数字的情况下
            {
                var ds = GetDataSetByWhere(_entityattr.Primarykey + " in (" + string.Join(",", keys.ToArray() as string[]) + ")");
                return ds.Tables[0].Rows.Cast<DataRow>().Sum(row => DeleteByKey(row[_entityattr.Primarykey]));
            }
            else
            {
                var ds = GetDataSetByWhere(_entityattr.Primarykey + " in (" + string.Join(",", keys.Select(x => "'" + x + "'").ToArray() as string[]) + ")");
                return ds.Tables[0].Rows.Cast<DataRow>().Sum(row => DeleteByKey(row[_entityattr.Primarykey]));
            }
        }
        /// <summary>
        /// 用此方法删除数据将不回触发OnDeling,OnDeled
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual int UnSafeDel(string where)
        {
            return dao.DeleteByWhere(new SqlParam(where));
        }
        //done
        /// <summary>
        /// 根據Where 条件删除数据
        /// </summary>
        /// <param name="where">"Column=columnValue" 同sql后Where条件一样</param>
        /// <returns></returns>
        public virtual int DeleteByWhere(string where)
        {
            var ds = GetDataSetByWhere(where);
            return ds.Tables[0].Rows.Cast<DataRow>().Sum(row => DeleteByKey(row[_entityattr.Primarykey]));
        }

        /// <summary>
        /// 根据where条件获取记录条数
        /// </summary>
        /// <param name="pr"></param>
        /// <returns></returns>
        /// done
        public virtual int GetCount(string where)
        {
            return dao.Count(new SqlParam(where));
        }
        //done
        /// <summary>
        /// 根据Key值判别数据库中是否存在这行数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual bool ExistsByKey(object key)
        {
            var pkpro = _propertys.Find(pro => pro.Name.ToLower() == _entityattr.Primarykey.ToLower());
            return ExistsByWhere(_entityattr.Primarykey + "=" + dao.GetDetail(new DbParam(_entityattr.Primarykey, key, _propertyattrs[pkpro].DataType)));
        }
        //done
        /// <summary>
        /// 根据Where条件判别数据是否存在
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual bool ExistsByWhere(string where)
        {
            return dao.Exists(new SqlParam(where));
        }
        //done
        /// <summary>
        /// 根据Column的列名和值去判断数据库中是否存在这行数据，并且返回改行数据的Key值，key值需要用Out接收
        /// </summary>
        /// <param name="column">列名</param>
        /// <param name="value">值</param>
        /// <param name="key">out 的key</param>
        /// <returns></returns>
        public virtual bool ExistsByColumnOutKey(string column, string value, out object key)
        {
            var ds = dao.GetDataSet(1, "*", new SqlParam(column + "=" + dao.GetDetail(new DbParam(column, value))), "");
            if (ds.Tables[0].Rows.Count > 0)
            {
                key = ds.Tables[0].Rows[0][_entityattr.Primarykey];
                return true;
            }
            else
            {
                key = null;
                return false;
            }
        }
        //done
        /// <summary>
        /// 根据Key值来获取一个Model实体
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual T GetDetailByKey(object key)
        {
            var ds = dao.GetDataSet(1, "*", new SqlParam(_entityattr.Primarykey + "=" + dao.GetDetail(new DbParam(_entityattr.Primarykey, key))), "");
            if (ds.Tables[0].Rows.Count > 0)
            {
                T obj = Activator.CreateInstance(_type) as T;
                obj.Extend(ds.Tables[0].Rows[0]);
                return obj;
            }
            else
            {
                return null;
            }
        }
        //done
        /// <summary>
        /// 根据DataSet中的DataRow来转化为实体对象
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        public virtual T GetModelByDataRow(DataRow row)
        {
            if (row != null)
            {
                T obj = Activator.CreateInstance(_type) as T;
                obj.Extend(row);
                return obj;
            }
            else
            {
                return null;
            }
        }
        //done
        /// <summary>
        /// 根据DataSet来获取实体对象
        /// </summary>
        /// <param name="dataset"></param>
        /// <returns></returns>
        public virtual T GetModelByDataSet(DataSet dataset)
        {
            if (dataset == null || dataset.Tables.Count == 0 || dataset.Tables[0].Rows.Count == 0)
            {
                return null;
            }
            else
            {
                return GetModelByDataRow(dataset.Tables[0].Rows[0]);
            }
        }

        //done
        /// <summary>
        /// 根据DataSet来获取并返回一个对象集合
        /// </summary>
        /// <param name="dataset"></param>
        /// <returns></returns>
        public virtual List<T> GetListModelByDataSet(DataSet dataset)
        {
            if (dataset == null || dataset.Tables.Count == 0 || dataset.Tables[0].Rows.Count == 0)
            {
                return null;
            }
            else
            {
                List<T> tlist = new List<T>();
                foreach (DataRow row in dataset.Tables[0].Rows)
                {
                    tlist.Add(GetModelByDataRow(row));
                }
                return tlist;
            }
        }
        //done
        /// <summary>
        /// 根据Where条件来获取Model实体
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual T GetModelByWhere(string where)
        {
            var dataset = GetDataSetByWhere(where);
            return GetModelByDataSet(dataset);
        }
        /// <summary>
        /// 根據Where條件獲取List集合
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        /// done
        public virtual List<T> GetListByWhere(string where)
        {
            DataSet set = GetDataSetByWhere(where);
            if (set.Tables[0].Rows.Count <= 0)
            {
                return new List<T>();
            }
            return GetListModelByDataSet(set);
        }
        //done
        /// <summary>
        /// 获取该表所有的集合
        /// </summary>
        /// <returns></returns>
        public virtual DataSet GetDataSet()
        {
            return dao.GetDataSet(0, "*", null, null);
        }
        /// <summary>
        /// 根据Where条件来获取该实体对应表的数据集DataSet
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByWhere(string where)
        {
            return dao.GetDataSet(0, "*", new SqlParam(where), null);
        }
        //done
        /// <summary>
        /// 根据Where条件获取想要的对应列的数据集，每个列名之间用","分割 eg:"column1,column2"
        /// </summary>
        /// <param name="where"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByWhereColumns(string where, string columns)
        {
            return dao.GetDataSet(0, columns, where, null);
        }
        //done
        /// <summary>
        /// 根据条件获取前多少条数据
        /// </summary>
        /// <param name="topn"></param>
        /// <param name="columns"></param>
        /// <param name="where"></param>
        /// <param name="ordreby"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByWhereOrderBy(int topn, string columns, string where, string ordreby)
        {
            return dao.GetDataSet(topn, columns, where, ordreby);
        }
        //done
        /// <summary>
        /// 根据GroupBy来获取资料,有where条件
        /// </summary>
        /// <param name="topn">前多少行数据</param>
        /// <param name="columns">需要查询的列，ex:'column1,column2'</param>
        /// <param name="where"></param>
        /// <param name="orderby"></param>
        /// <param name="groupby"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByGroup(int topn, string columns, string where, string orderby, string groupby)
        {
            return dao.GetDataSetByGroup(topn, columns, where, orderby, groupby);
        }
        //done
        /// <summary>
        /// 根据Orderby和GroupBy 来获取前多少条数据
        /// </summary>
        /// <param name="topn"></param>
        /// <param name="columns"></param>
        /// <param name="orderby"></param>
        /// <param name="groupby"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByGroup(int topn, string columns, string orderby, string groupby)
        {
            return dao.GetDataSetByGroup(topn, columns, null, orderby, groupby);
        }
        //done
        /// <summary>
        /// 根据Orderby和GroupBy 来获取前数据，不需要某些条件时候可以用""替代对应的形参值
        /// </summary>
        /// <param name="columns"></param>
        /// <param name="orderby"></param>
        /// <param name="groupby"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByGroup(string columns, string orderby, string groupby)
        {
            return dao.GetDataSetByGroup(0, columns, null, orderby, groupby);
        }
        //done
        //done
        /// <summary>
        /// 根据Orderby和GroupBy 来获取前数据
        /// </summary>
        /// <param name="columns"></param>
        /// <param name="orderby"></param>
        /// <param name="groupby"></param>
        /// <returns></returns>
        public virtual DataSet GetDataSetByGroup(string columns, string groupby)
        {
            return dao.GetDataSetByGroup(0, columns, null, "", groupby);
        }
        //done
        /// <summary>
        /// 连表查询，并且获取前多少条数据，如果为0，则获取满足条件的全部数据
        /// </summary>
        /// <param name="topn">条数</param>
        /// <param name="columns">列名</param>
        /// <param name="where">where条件，eg:"columname=value"<param>
        /// <param name="ordreby">根据哪一列来排序</param>
        /// <param name="tablename">要连接的另一张表</param>
        /// <param name="on">连表的On条件 eg：" a.id =b.id",a,b中a是定死的,b可以在自己传Table的时候改：eg："GetJoinDataSet(10,"colunm1,column2","a.property='123'","a.id","tableH h","a.id=h.id")"</param>
        /// <returns></returns>
        public virtual DataSet GetJoinDataSet(int topn, string columns, string where, string ordreby, string tablename, string on)
        {
            return dao.GetJoinDataSet(topn, columns, where, ordreby, tablename, on);
        }
        //done
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <returns></returns>
        public virtual KeyValuePair<DataSet, int> GetPageDateSet(int page, int pagesize)
        {
            return dao.GetPageDateSet(page, pagesize, null, "*", null, _entityattr.Primarykey);
        }
        //done
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <returns></returns>
        public virtual KeyValuePair<DataSet, int> GetPageDateSet(int page, int pagesize, string where)
        {
            return dao.GetPageDateSet(page, pagesize, where, "*", null, _entityattr.Primarykey);
        }
        //done
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <returns></returns>
        public virtual KeyValuePair<DataSet, int> GetPageDateSet(int page, int pagesize, string where, string columns)
        {
            return dao.GetPageDateSet(page, pagesize, where, columns, null, _entityattr.Primarykey);
        }
        //done
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <param name="where"></param>
        /// <param name="columns"></param>
        /// <param name="ordreby"></param>
        /// <returns></returns>
        public virtual KeyValuePair<DataSet, int> GetPageDateSet(int page, int pagesize, string where, string columns, string ordreby)
        {
            return dao.GetPageDateSet(page, pagesize, where, columns, ordreby, _entityattr.Primarykey);
        }
        /// <summary>
        /// 连表分页查询
        /// </summary>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <param name="columns"></param>
        /// <param name="tablename"></param>
        /// <param name="where"></param>
        /// <param name="ordreby"></param>
        /// <returns></returns>
        public virtual KeyValuePair<DataSet, int> GetJoinPageDateSet(int page, int pagesize, string columns, string tablename, string where, string ordreby, string on)
        {
            return dao.GetJoinPageDateSet(page, pagesize, columns, where, ordreby, tablename, on);
        }



        protected bool KeyIsNum(DbParam db)
        {
            if (db.DataType == DbDataType.BigInt)
            {
                return true;
            }
            else
            {
                return false;
            }

        }

        #endregion

        #region 扩展方法
        public virtual string GetTitle(object key)
        {
            T model = GetDetailByKey(key);
            if (model == null)
            {
                return "";
            }
            else
            {
                if (_propertyattrs.ToList().Exists(kv => kv.Value.SqlName.ToLower() == _entityattr.TitleColumn.ToLower()))
                {
                    return (model.Get(_propertyattrs.ToList().Find(kv => kv.Value.SqlName.ToLower() == _entityattr.TitleColumn.ToLower()).Key.Name) ?? "").ToString();
                }
                else
                {
                    return null;
                }
            }
        }
        public virtual string GetTitle(object key, string column)
        {
            T model = GetDetailByKey(key);
            if (model == null)
            {
                return "";
            }
            else
            {
                if (_propertyattrs.ToList().Exists(kv => kv.Value.SqlName.ToLower() == column.ToLower()))
                {
                    return (model.Get(_propertyattrs.ToList().Find(kv => kv.Value.SqlName.ToLower() == column.ToLower()).Key.Name) ?? "").ToString();
                }
                else
                {
                    return null;
                }
            }
        }
        public virtual object[] GetKeysByTitle(string title)
        {
            if (_propertyattrs.ToList().Exists(kv => kv.Value.SqlName.ToLower() == _entityattr.TitleColumn.ToLower()))
            {
                var ds = GetDataSetByWhere(_entityattr.TitleColumn + " like " + dao.GetDetail(new DbParam(_entityattr.TitleColumn, "%" + title + "%")));
                var result = new List<object>();
                foreach (DataRow row in ds.Tables[0].Rows)
                {
                    result.Add(row[_entityattr.Primarykey]);
                }
                if (result.Count == 0)
                {
                    return null;
                }
                else
                {
                    return result.ToArray();
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

    }
}
